package Renderer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.media.opengl.GL2;
import LDraw.Support.GLMatrixMath;
import LDraw.Support.LDrawGlobalFlag;
import LDraw.Support.MatrixMath;
import com.jogamp.common.nio.Buffers;
public class LDrawDLSession {
public static final int INST_RING_BUFFER_COUNT = 4; // Number of VBOs to
// rotate for hw
// instancing - doesn't
// actually help, it
// turns out.
/**
* @uml.property name="dl_head"
* @uml.associationEnd
*/
LDrawDL dl_head; // Linked list of all DLs that will be instance-drawn, with
// count.
/**
* @uml.property name="dl_count"
*/
int dl_count;
/**
* @uml.property name="sorted_head"
* @uml.associationEnd
*/
LDrawDLSortedInstanceLink sorted_head; // Linked list + count for DLs being
// drawn later to Z sort.
/**
* @uml.property name="sort_count"
*/
int sort_count;
/**
* @uml.property name="model_view"
*/
float model_view[]; // Model-view matrix, used to Z sort translucent
// objects.
/**
* @uml.property name="inst_ring"
*/
int inst_ring; // If using more than one instancing buffer, this tells which
// one we use.
// ========== LDrawDLSessionCreate
// ================================================
//
// Purpose: Create a new drawing Drawing sessions sit entirely in a
// BDP
// for speed - most of our linked lists are just null.
//
// ================================================================================
public static IntBuffer inst_vbo_ring[] = new IntBuffer[INST_RING_BUFFER_COUNT];
public static int inst_ring_last = 0;
public LDrawDLSession(float model_view[]){
dl_head = null;
dl_count = 0;
sorted_head = null;
sort_count = 0;
this.model_view = new float[16];
System.arraycopy(model_view, 0, this.model_view, 0,
model_view.length);
inst_ring = inst_ring_last;
// each session picks up a new buffer in the ring of instance buffers.
inst_ring_last = (inst_ring_last + 1) % INST_RING_BUFFER_COUNT;
}
/**
* @return
* @uml.property name="dl_head"
*/
public LDrawDL getDl_head() {
return dl_head;
}
/**
* @param dl_head
* @uml.property name="dl_head"
*/
public void setDl_head(LDrawDL dl_head) {
this.dl_head = dl_head;
}
/**
* @return
* @uml.property name="dl_count"
*/
public int getDl_count() {
return dl_count;
}
/**
* @param dl_count
* @uml.property name="dl_count"
*/
public void setDl_count(int dl_count) {
this.dl_count = dl_count;
}
/**
* @return
* @uml.property name="sorted_head"
*/
public LDrawDLSortedInstanceLink getSorted_head() {
return sorted_head;
}
/**
* @param sorted_head
* @uml.property name="sorted_head"
*/
public void setSorted_head(LDrawDLSortedInstanceLink sorted_head) {
this.sorted_head = sorted_head;
}
/**
* @return
* @uml.property name="sort_count"
*/
public int getSort_count() {
return sort_count;
}
/**
* @param sort_count
* @uml.property name="sort_count"
*/
public void setSort_count(int sort_count) {
this.sort_count = sort_count;
}
/**
* @return
* @uml.property name="model_view"
*/
public float[] getModel_view() {
return model_view;
}
/**
* @param model_view
* @uml.property name="model_view"
*/
public void setModel_view(float[] model_view) {
this.model_view = model_view;
}
/**
* @return
* @uml.property name="inst_ring"
*/
public int getInst_ring() {
return inst_ring;
}
/**
* @param inst_ring
* @uml.property name="inst_ring"
*/
public void setInst_ring(int inst_ring) {
this.inst_ring = inst_ring;
}
// ========== LDrawDLSessionDrawAndDestroy
// ========================================
//
// Purpose: Draw any DLs that were deferred during drawing, then nuke the
// session object.
//
// ================================================================================
public void drawAndDestroy(GL2 gl2) {
LDrawDLInstance inst;
LDrawDL dl;
// INSTANCED DRAWING CASE
if (dl_head != null) {
// Build a var-sized array of segments to record our instances for
// hardware instancing. We may not need it for every DL but that's
// okay.
LDrawDLSegment[] segments = new LDrawDLSegment[dl_count];
for (LDrawDLSegment segment : segments)
segment = new LDrawDLSegment();
int segmentIndex = 0;
LDrawDLSegment cur_segment = segments[segmentIndex];
// If we do not yet have a VBO for instancing, build one now.
if (inst_vbo_ring[inst_ring].get(0) == 0)
gl2.glGenBuffers(1, inst_vbo_ring[inst_ring]);
// Map our instance buffer so we can write instancing data.
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER,
inst_vbo_ring[inst_ring].get(0));
gl2.glBufferData(GL2.GL_ARRAY_BUFFER, LDrawDisplayList.INST_MAX_COUNT * Float.SIZE
/ 8 * 24, null, GL2.GL_DYNAMIC_DRAW);
ByteBuffer inst_base = gl2.glMapBuffer(GL2.GL_ARRAY_BUFFER,
GL2.GL_WRITE_ONLY);
float[] inst_data = inst_base.asFloatBuffer().array();
int inst_remain = LDrawDisplayList.INST_MAX_COUNT;
// Main loop 1: we will walk every instanced DL and either
// accumulate its instances (for hardware instancing) or just draw
// now
// (For attribute instancing).
while (dl_head != null) {
dl = dl_head;
if (dl.instance_count >= LDrawDisplayList.get_instance_cutoff(gl2)
&& inst_remain >= dl.instance_count) {
// If we have capacity for hw instancing and this DL is used
// enough, create a segment record and fill it out.
cur_segment.geo_vbo = dl.geo_vbo;
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
cur_segment.idx_vbo = dl.idx_vbo;
}
cur_segment.dl = dl.texes[0];
// todo
// cur_segment.inst_base = null;
// cur_segment.inst_base += (inst_data - inst_base);
cur_segment.inst_count = dl.instance_count;
// Now walk the instance list, copying the instances into
// the instance VBO one by one.
int inst_data_offset = 0;
for (inst = dl.instance_head; inst != null; inst = inst.next) {
MatrixMath.copy_vec4(inst_data, inst_data_offset, inst.color, 0);
MatrixMath.copy_vec4(inst_data, inst_data_offset + 4, inst.comp, 0);
inst_data[inst_data_offset + 8] = inst.transform[0]; // Note:
// copy
// on
// transpose
// to
// get
// matrix
// into
// right
// form!
inst_data[inst_data_offset + 9] = inst.transform[4];
inst_data[inst_data_offset + 10] = inst.transform[8];
inst_data[inst_data_offset + 11] = inst.transform[12];
inst_data[inst_data_offset + 12] = inst.transform[1];
inst_data[inst_data_offset + 13] = inst.transform[5];
inst_data[inst_data_offset + 14] = inst.transform[9];
inst_data[inst_data_offset + 15] = inst.transform[13];
inst_data[inst_data_offset + 16] = inst.transform[2];
inst_data[inst_data_offset + 17] = inst.transform[6];
inst_data[inst_data_offset + 18] = inst.transform[10];
inst_data[inst_data_offset + 19] = inst.transform[14];
inst_data[inst_data_offset + 20] = inst.transform[3];
inst_data[inst_data_offset + 21] = inst.transform[7];
inst_data[inst_data_offset + 22] = inst.transform[11];
inst_data[inst_data_offset + 23] = inst.transform[15];
inst_data_offset += 24;
--inst_remain;
}
cur_segment = segments[++segmentIndex];
} else {
// Immediate mode instancing - we draw now! So bind up the
// mesh of this DL.
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, dl.geo_vbo.get(0));
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER,
dl.idx_vbo.get(0));
}
// float * p = null;
ByteBuffer p = Buffers
.newDirectByteBuffer(10 * Float.SIZE / 8);
gl2.glVertexAttribPointer(
AttributeT.attr_position.getValue(), 3,
GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8,
p);
gl2.glVertexAttribPointer(
AttributeT.attr_normal.getValue(), 3, GL2.GL_FLOAT,
false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(AttributeT.attr_color.getValue(),
4, GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE
/ 8, p);
// Now walk the instance list...push instance data into
// attributes in immediate mode and draw.
for (inst = dl.instance_head; inst != null; inst = inst.next) {
int i;
for (i = 0; i < 4; ++i)
gl2.glVertexAttrib4f(
AttributeT.attr_transform_x.getValue() + i,
inst.transform[i], inst.transform[4 + i],
inst.transform[8 + i],
inst.transform[12 + i]);
gl2.glVertexAttrib4fv(
AttributeT.attr_color_current.getValue(),
inst.color, 0);
gl2.glVertexAttrib4fv(
AttributeT.attr_color_compliment.getValue(),
inst.comp, 0);
LDrawDLPerTex tptr = dl.texes[0];
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
if (tptr.line_count != 0)
gl2.glDrawElements(GL2.GL_LINES,
tptr.line_count, GL2.GL_UNSIGNED_INT,
tptr.line_off);
if (tptr.tri_count != 0)
gl2.glDrawElements(GL2.GL_TRIANGLES,
tptr.tri_count, GL2.GL_UNSIGNED_INT,
tptr.tri_off);
if (tptr.quad_count != 0)
gl2.glDrawElements(GL2.GL_QUADS,
tptr.quad_count, GL2.GL_UNSIGNED_INT,
tptr.quad_off);
}
}
}
dl.instance_head = dl.instance_tail = null;
dl.instance_count = 0;
if (dl.flags.get(LDrawDLT.dl_needs_destroy)) {
dl.destroy(gl2);
}
dl_head = dl.next_dl;
dl.next_dl = null;
}
// Hardware instancing: unmap our hardware instance buffer and if we
// got data,
// set up the GPU for hardware instancing.
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER,
inst_vbo_ring[inst_ring].get(0));
gl2.glUnmapBuffer(GL2.GL_ARRAY_BUFFER);
if (segments[segmentIndex] != cur_segment) {
gl2.glEnableVertexAttribArray(AttributeT.attr_transform_x
.getValue());
gl2.glEnableVertexAttribArray(AttributeT.attr_transform_y
.getValue());
gl2.glEnableVertexAttribArray(AttributeT.attr_transform_z
.getValue());
gl2.glEnableVertexAttribArray(AttributeT.attr_transform_w
.getValue());
gl2.glEnableVertexAttribArray(AttributeT.attr_color_current
.getValue());
gl2.glEnableVertexAttribArray(AttributeT.attr_color_compliment
.getValue());
// todo
// gl2.glVertexAttribDivisorARB(AttributeT.attr_transform_x.getValue(),1);
// gl2.glVertexAttribDivisorARB(AttributeT.attr_transform_y.getValue(),1);
// gl2.glVertexAttribDivisorARB(AttributeT.attr_transform_z.getValue(),1);
// gl2.glVertexAttribDivisorARB(AttributeT.attr_transform_w.getValue(),1);
// gl2.glVertexAttribDivisorARB(AttributeT.attr_color_current.getValue(),1);
// gl2.glVertexAttribDivisorARB(AttributeT.attr_color_compliment.getValue(),1);
// Main loop 2 over DLs - for each DL that had hw-instances we
// built a segment
// in our array. Bind the DL itself, as well as the instance
// pointers, and do an instanced-draw.
LDrawDLSegment s;
segmentIndex = 0;
for (s = segments[0]; s != cur_segment; s = segments[++segmentIndex]) {
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, s.geo_vbo.get(0));
if (LDrawGlobalFlag.WANT_SMOOTH != false)
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER,
s.idx_vbo.get(0));
// float [] p = null;
ByteBuffer p = Buffers
.newDirectByteBuffer(10 * Float.SIZE / 8);
gl2.glVertexAttribPointer(
AttributeT.attr_position.getValue(), 3,
GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8,
p);
gl2.glVertexAttribPointer(
AttributeT.attr_normal.getValue(), 3, GL2.GL_FLOAT,
false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(AttributeT.attr_color.getValue(),
4, GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE
/ 8, p);
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER,
inst_vbo_ring[inst_ring].get());
p = ByteBuffer.allocate(4 * Float.SIZE / 8);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(0 + i, p.get(i));
p.clear();
gl2.glVertexAttribPointer(
AttributeT.attr_color_compliment.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(4 + i, p.get(i));
p.clear();
gl2.glVertexAttribPointer(
AttributeT.attr_transform_x.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(8 + i, p.get(i));
p.clear();
gl2.glVertexAttribPointer(
AttributeT.attr_transform_y.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(12 + i, p.get(i));
p.clear();
gl2.glVertexAttribPointer(
AttributeT.attr_transform_z.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(16 + i, p.get(i));
p.clear();
gl2.glVertexAttribPointer(
AttributeT.attr_transform_w.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
gl2.glVertexAttribPointer(
AttributeT.attr_color_current.getValue(), 4,
GL2.GL_FLOAT, false, 24 * Float.SIZE / 8, p);
for (int i = 0; i < 4 * Float.SIZE / 8; i++)
s.inst_base.put(20 + i, p.get(i));
p.clear();
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
if (s.dl.line_count != 0)
gl2.glDrawElementsInstanced(GL2.GL_LINES,
s.dl.line_count, GL2.GL_UNSIGNED_INT,
s.dl.line_off, s.inst_count);
if (s.dl.tri_count != 0)
gl2.glDrawElementsInstanced(GL2.GL_TRIANGLES,
s.dl.tri_count, GL2.GL_UNSIGNED_INT,
s.dl.tri_off, s.inst_count);
if (s.dl.quad_count != 0)
gl2.glDrawElementsInstanced(GL2.GL_QUADS,
s.dl.quad_count, GL2.GL_UNSIGNED_INT,
s.dl.quad_off, s.inst_count);
} else {
if (s.dl.line_count != 0)
gl2.glDrawArraysInstanced(GL2.GL_LINES,
s.dl.line_off, s.dl.line_count,
s.inst_count);
if (s.dl.tri_count != 0)
gl2.glDrawArraysInstanced(GL2.GL_TRIANGLES,
s.dl.tri_off, s.dl.tri_count, s.inst_count);
if (s.dl.quad_count != 0)
gl2.glDrawArraysInstanced(GL2.GL_QUADS,
s.dl.quad_off, s.dl.quad_count,
s.inst_count);
}
}
gl2.glDisableVertexAttribArray(AttributeT.attr_transform_x
.getValue());
gl2.glDisableVertexAttribArray(AttributeT.attr_transform_y
.getValue());
gl2.glDisableVertexAttribArray(AttributeT.attr_transform_z
.getValue());
gl2.glDisableVertexAttribArray(AttributeT.attr_transform_w
.getValue());
gl2.glDisableVertexAttribArray(AttributeT.attr_color_current
.getValue());
gl2.glDisableVertexAttribArray(AttributeT.attr_color_compliment
.getValue());
// todo
// gl2.glVertexAttribDivisorARB(attr_transform_x,0);
// gl2.glVertexAttribDivisorARB(attr_transform_y,0);
// gl2.glVertexAttribDivisorARB(attr_transform_z,0);
// gl2.glVertexAttribDivisorARB(attr_transform_w,0);
// gl2.glVertexAttribDivisorARB(attr_color_current,0);
// gl2.glVertexAttribDivisorARB(attr_color_compliment,0);
}
}
// MAIN LOOP 3: sorted deferred drawing (!)
LDrawDLSortedInstanceLink l;
if (sorted_head != null) {
// If we have any sorting to do, allocate an array of the size of
// all sorted geometry for sorting purposes.
LDrawDLSortedInstanceLink arr[] = new LDrawDLSortedInstanceLink[sort_count];
for (int i = 0; i < sort_count; i++)
arr[i] = new LDrawDLSortedInstanceLink();
LDrawDLSortedInstanceLink p = arr[0];
int index_arr = 0;
// Copy each sorted instance into our array. "Eval" is the
// measurement of distance - calculate eye-space Z and use that.
for (l = sorted_head; l != null; l = l.next) {
float v[] = { l.transform[12], l.transform[13],
l.transform[14], 1.0f };
try {
arr[index_arr] = (LDrawDLSortedInstanceLink) l.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
p = arr[index_arr];
// memcpy(p,l,sizeof(LDrawDLSortedInstanceLink));
float v_eye[] = new float[4];
GLMatrixMath.applyMatrix(v_eye, model_view, v);
p.eval = v_eye[2];
p = arr[++index_arr];
}
// Now: sort our array ascending to get far to near in eye space.
// todo
// qsort(arr,sort_count,sizeof(LDrawDLSortedInstanceLink),compare_sorted_link);
// NOW we can walk our sorted array and draw each brick, 1x1. This
// code is a rehash of the "draw now"
// code in LDrawDLDraw and could be factored.
index_arr = 0;
l = arr[index_arr];
int lc;
for (lc = 0; lc < sort_count; ++lc) {
int i;
for (i = 0; i < 4; ++i)
gl2.glVertexAttrib4f(AttributeT.attr_transform_x.getValue()
+ i, l.transform[i], l.transform[4 + i],
l.transform[8 + i], l.transform[12 + i]);
gl2.glVertexAttrib4fv(AttributeT.attr_color_current.getValue(),
l.color, 0);
gl2.glVertexAttrib4fv(
AttributeT.attr_color_compliment.getValue(), l.comp, 0);
dl = l.dl;
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, dl.geo_vbo.get(0));
if (LDrawGlobalFlag.WANT_SMOOTH != false)
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER,
dl.idx_vbo.get(0));
ByteBuffer p2 = ByteBuffer.allocate(LDrawDisplayList.VERT_STRIDE * Float.SIZE
/ 8);
gl2.glVertexAttribPointer(AttributeT.attr_position.getValue(),
3, GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8,
p2);
gl2.glVertexAttribPointer(AttributeT.attr_normal.getValue(), 3,
GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8, p2);
gl2.glVertexAttribPointer(AttributeT.attr_color.getValue(), 4,
GL2.GL_FLOAT, false, LDrawDisplayList.VERT_STRIDE * Float.SIZE / 8, p2);
LDrawDLPerTex tptr = dl.texes[0];
int tptrIndex = 0;
int t;
for (t = 0; t < dl.tex_count; ++t, tptr = dl.texes[++tptrIndex]) {
if (tptr.spec.tex_obj != 0) {
LDrawDisplayList.setup_tex_spec(gl2, tptr.spec);
} else
LDrawDisplayList.setup_tex_spec(gl2, l.spec);
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
if (tptr.line_count != 0)
gl2.glDrawElements(GL2.GL_LINES, tptr.line_count,
GL2.GL_UNSIGNED_INT, tptr.line_off);
if (tptr.tri_count != 0)
gl2.glDrawElements(GL2.GL_TRIANGLES,
tptr.tri_count, GL2.GL_UNSIGNED_INT,
tptr.tri_off);
if (tptr.quad_count != 0)
gl2.glDrawElements(GL2.GL_QUADS, tptr.quad_count,
GL2.GL_UNSIGNED_INT, tptr.quad_off);
} else {
if (tptr.line_count != 0)
gl2.glDrawArrays(GL2.GL_LINES, tptr.line_off,
tptr.line_count);
if (tptr.tri_count != 0)
gl2.glDrawArrays(GL2.GL_TRIANGLES, tptr.tri_off,
tptr.tri_count);
if (tptr.quad_count != 0)
gl2.glDrawArrays(GL2.GL_QUADS, tptr.quad_off,
tptr.quad_count);
}
}
l = l.next;
}
}
gl2.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
if (LDrawGlobalFlag.WANT_SMOOTH != false) {
gl2.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
}
// Finally done - all allocations for session (including our own obj)
// come from a BDP, so cleanup is quick.
// Instance VBO remains to be reused.
// DLs themselves live on beyond
// LDrawBDPDestroy(alloc);
}// end LDrawDLSessionDrawAndDestroy
}